// file: Midi.c
// author: Sebastian Steppeler copyright 2012
/*license: This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.*/

// common includes:
#include <avr/io.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <util/delay.h>
#include <avr/wdt.h>
#include <avr/interrupt.h>
// private includes:
#include "main.h"
#include "typedefs.h"
#include "Midi.h"
#include "Timer.h"

#define BUFFER_LENGTH 90
volatile char Buffer[BUFFER_LENGTH];
volatile char cWriteCur, cReadCur;

UCHAR cReceivedData[3];
UCHAR cReceiveCur = 0;

// externs:
extern int iSustain;
extern volatile int iCounterTest;
volatile char cCalibration = CALIB_CANCEL;

void InitMidi(void)
{
	// init uart:
	UCSRB |= (1<<TXEN) | (1<<RXEN) | (1<<RXCIE);// |(1<<TXCIE);  // UART TX einschalten
  	UCSRC |= (1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);  // Asynchron 8N1 
 
  	UBRRH = UBRR_VAL >> 8;
  	UBRRL = UBRR_VAL & 0xFF;
}

void HandleController(UCHAR cControl, int iVal)
{
	switch(cControl)
	{
		case PDP_PEDAL_SUSTAIN:
		{
			SendMidiCommand(0xB0, 0, 64, iVal);
			
		} break;
		case PDP_PEDAL_SOSTENUTO:
		{
			SendMidiCommand(0xB0, 0, 66, iVal);
		} break;
		case PDP_PEDAL_SOFT:
		{
			SendMidiCommand(0xB0, 0, 67, iVal);
		} break;		
	}
}

void SendMidiCommand(UCHAR cCmd, UCHAR cChannel, UCHAR cController, UCHAR cValue)
{
	cCmd |= cChannel;
	if( (cController > 127) || (cValue > 127) )
		return;

	unsigned char tmp_sreg;
	tmp_sreg = SREG; 
	cli();

	
	char cTemp;
	cTemp = cWriteCur+1;
	if(cTemp>=BUFFER_LENGTH)
		cTemp = 0;
	if(cTemp==cReadCur)
	{
		if(tmp_sreg & (128))
		sei();
		return;
	}
	cTemp++;
	if(cTemp>=BUFFER_LENGTH)
		cTemp = 0;
	if(cTemp==cReadCur)
	{
		if(tmp_sreg & (128))
			sei();
		return;
	}

	cTemp++;
	if(cTemp>=BUFFER_LENGTH)
		cTemp = 0;
	if(cTemp==cReadCur)
	{
		if(tmp_sreg & (128))
			sei();
		return;
	}	
	
	
	AddCharToQueue(cCmd);
	AddCharToQueue(cController);
	AddCharToQueue(cValue);	
	
	//USB_SendMIDIMessage(cCmd, cChannel, cController, cValue);	

	if(tmp_sreg & (128))
		sei();	
}

void NoteOn(UCHAR cNote, UCHAR cVelocity)
{
	if(cNote>127)
		cNote = 127;
	if(cVelocity>127)
		cVelocity = 127;

	unsigned char tmp_sreg;
	tmp_sreg = SREG; 
	cli();

	
	char cTemp;
	cTemp = cWriteCur+1;
	if(cTemp>=BUFFER_LENGTH)
		cTemp = 0;
	if(cTemp==cReadCur)
	{
		if(tmp_sreg & (128))
		sei();
		return;
	}
	cTemp++;
	if(cTemp>=BUFFER_LENGTH)
		cTemp = 0;
	if(cTemp==cReadCur)
	{
		if(tmp_sreg & (128))
			sei();
		return;
	}

	cTemp++;
	if(cTemp>=BUFFER_LENGTH)
		cTemp = 0;
	if(cTemp==cReadCur)
	{
		if(tmp_sreg & (128))
			sei();
		return;
	}	
	
	
	AddCharToQueue(0x90);
	AddCharToQueue(cNote);
	AddCharToQueue(cVelocity);	
	
	if(tmp_sreg & (128))
		sei();
}

void NoteOff(UCHAR cNote, UCHAR cVelocity)
{
	if(cNote>127)
		cNote = 127;
	if(cVelocity>127)
		cVelocity = 127;

	unsigned char tmp_sreg;
	tmp_sreg = SREG; 
	cli();

	char cTemp;
	cTemp = cWriteCur+1;
	if(cTemp>=BUFFER_LENGTH)
		cTemp = 0;
	if(cTemp==cReadCur)
	{
		if(tmp_sreg & (128))
		sei();
		return;
	}

	cTemp++;
	if(cTemp>=BUFFER_LENGTH)
		cTemp = 0;
	if(cTemp==cReadCur)
	{
		if(tmp_sreg & (128))
			sei();
		return;
	}

	cTemp++;
	if(cTemp>=BUFFER_LENGTH)
		cTemp = 0;
	if(cTemp==cReadCur)
	{
		if(tmp_sreg & (128))
			sei();
		return;
	}

	
	
	AddCharToQueue(0x80);
	AddCharToQueue(cNote);
	AddCharToQueue(cVelocity);		
	
	if(tmp_sreg & (128))
		sei();
}


void AddCharToQueue(char cData)
{
	unsigned char tmp_sreg;
 
   	tmp_sreg = SREG; 
	cli();
	Buffer[(int)cWriteCur] = cData;

	if(UCSRA & (1<<UDRE))
	{
		UDR = Buffer[(int)cReadCur];
		cReadCur++;
		if(cReadCur>=BUFFER_LENGTH)
			cReadCur = 0;
	}
	else
	{
		// enable UDR empty interrupt:
		UCSRB |= (1<<UDRIE);
	}
	
	cWriteCur++;
	if(cWriteCur>=BUFFER_LENGTH)
		cWriteCur = 0;
	
	if(tmp_sreg & (128))
		sei();
}




ISR(USART_UDRE_vect)
{
	UDR = Buffer[(int)cReadCur];
	cReadCur++;
	if(cReadCur>=BUFFER_LENGTH)
		cReadCur = 0;
		
	// when the buffer is empty we can stop the interrupt:
	if(cReadCur==cWriteCur)
		UCSRB &= ~(1<<UDRIE);
	
}

// receive midi messages and simply forward them.
ISR(USART_RXC_vect)
{
	char cTemp;
	
	cTemp = UDR;
	// is preamble?
	if(cTemp & 0x80)
		cReceiveCur = 0;
		
	// collect data in 1 midi message buffer
	cReceivedData[cReceiveCur] = cTemp;	
	cReceiveCur++;
	
	// send buffer when complete:
	if(cReceiveCur==3)
	{
		// handle control notes:
		if((cReceivedData[0]==0x90) && (cReceivedData[1]==68))
			cCalibration = CALIB_RUN;
		else if((cReceivedData[0]==0x90) && (cReceivedData[1]==61))
			cCalibration = CALIB_CANCEL;
		else if((cReceivedData[0]==0x90) && (cReceivedData[1]==70))
			cCalibration = CALIB_SAVE;
		// forward midi message:	
		SendMidiCommand(cReceivedData[0], 0, cReceivedData[1], cReceivedData[2]);
	}		
	// do never send until new preamble is received:
	else if(cReceiveCur>3)
		cReceiveCur = 4;
}

/*ISR(USART_TXC_vect)
{
	PORTC = 0x0f;
	PORTC = 0;
}*/
